<!--
 @description 附件上传
 @fileName AttachmentUpload.vue
 @author zengqiongying
 @created 2023/04/20 22:00:40
-->
<template>
  <div class="attachment-uploader">
    <div class="file-list">
      <slot v-bind="fileList">
        <template v-if="viewType === 'image'">
          <div class="file-images-card">
            <div class="f-img-item" v-for="f in fileList" :key="getKey(f)" :style="imageStyle">
              <div v-if="['failed', 'uploading'].includes(f.uploadStatus)" class="cus-progress">
                <qmp-circle
                  v-if="f.uploadStatus === 'uploading'"
                  :rate="100"
                  :size="60"
                  v-model:current-rate="f.progress"
                  :text="`${f.progress.toFixed(0)}%`"
                  color="#16BC83"
                />
                <qmp-circle
                  v-else-if="f.uploadStatus === 'failed' && !!f.rawFile"
                  :rate="100"
                  :size="60"
                  v-model:current-rate="f.progress"
                  :text="`${f.progress.toFixed(0)}%`"
                  color="#ee0a24"
                />
              </div>
              <div class="img-content">
                <img :src="getFileUrl(f)" />
                <span class="operator">
                  <span v-if="readonly" class="btn" @click.stop="handlerDownload(f)">
                    <qmp-svg-icon class="remove" name="xiazai" />
                  </span>
                  <template v-else>
                    <span class="btn" @click.stop="handlerRemove(f)">
                      <qmp-svg-icon class="remove" name="shanchulajixiang-line" />
                    </span>
                    <span
                      v-if="f.uploadStatus === 'failed' && !!f.rawFile"
                      class="btn"
                      @click.stop="handlerReupload(f)"
                    >
                      <qmp-svg-icon class="reupload" name="shuaxin" />
                    </span>
                  </template>
                </span>
              </div>
            </div>
          </div>
        </template>
        <ul v-else class="file-list-item">
          <li v-for="f in fileList" :key="getKey(f)">
            <div v-if="['failed', 'uploading'].includes(f.uploadStatus)" class="cus-progress">
              <qmp-progress v-if="f.uploadStatus === 'uploading'" :percentage="f.progress" color="#16BC83" />
              <qmp-progress
                v-else-if="f.uploadStatus === 'failed' && !!f.rawFile"
                :percentage="f.progress"
                color="#ee0a24"
              />
            </div>
            <div class="filename">
              <span class="name">{{ getFileName(f) }}</span>
              <span class="operator">
                <span v-if="readonly" class="btn" @click.stop="handlerDownload(f)">
                  <qmp-svg-icon class="remove" name="xiazai" />
                </span>
                <template v-else>
                  <span class="btn" @click.stop="handlerRemove(f)">
                    <qmp-svg-icon class="remove" name="shanchulajixiang-line" />
                  </span>
                  <span v-if="f.uploadStatus === 'failed' && !!f.rawFile" class="btn" @click.stop="handlerReupload(f)">
                    <qmp-svg-icon class="reupload" name="shuaxin" />
                  </span>
                </template>
              </span>
            </div>
          </li>
        </ul>
      </slot>
    </div>
    <div v-show="showUpload && !readonly" class="operator-area" @click.stop="handlerFileClick">
      <slot name="trigger">
        <div class="upload-btn">
          <qmp-icon name="plus" />
        </div>
      </slot>
      <input
        ref="fileEl"
        id="file-input"
        type="file"
        :accept="fileAccept"
        :multiple="multiple"
        style="display: none"
        @change="handlerFileChange"
      />
    </div>

    <qmp-popup v-if="popVisible" v-model:show="popVisible" position="bottom" round>
      <NativeSelectFile
        :visible="popVisible"
        :need-pic-num="limit <= 0 ? 1 : limit - fileList.length"
        @success="handlerFileChange"
      />
    </qmp-popup>
  </div>
</template>

<script lang="ts">
  import { defineComponent, reactive, ref, toRefs, computed, watch, nextTick, getCurrentInstance, onMounted } from 'vue'
  import NativeSelectFile from './NativeSelectFile.vue'
  import { queryId, getAttachmentTemplate, queryAttachmentList, downAttachment } from '@/apis/common'
  import { formatURL } from '@/utils'
  const { programmeFlag } = require('../../config/settings')
  import { createDownload } from '@/utils/download'
  export default defineComponent({
    name: 'AttachmentUpload',
    components: { NativeSelectFile },
    props: {
      modelValue: {
        type: Array,
        default: () => []
      },
      accept: {
        type: String,
        default: 'image/png,image/gif,image/jpeg'
      },
      objectId: {
        type: String,
        default: ''
      }, //对象ID
      typeCode: {
        type: String,
        default: ''
      }, //通过typeCode查询附件模板配置ID(configAttachmentId)
      multiple: {
        type: Boolean,
        default: false
      },
      limit: {
        type: Number,
        default: 0
      },
      autoGetId: {
        type: Boolean,
        default: false
      },
      readonly: {
        type: Boolean,
        default: false
      },
      delSource: {
        type: Boolean,
        default: false
      },
      otherParams: {
        type: Object,
        default: () => {}
      },
      viewType: {
        type: String,
        default: 'list'
      }, // 展示类型：list |image
      imageColumn: {
        type: Number,
        default: 1 // 图片列
      },
      imageHeight: {
        type: [Number, String],
        default: 100 // 图片高度
      }
    },
    setup(props: any, ctx: any) {
      const fileEl = ref()
      const { proxy }: any = getCurrentInstance()
      interface datasModle {
        fileList: any[]
        nextId: string
        token: string
        popVisible: boolean
        configAttachmentId: string
        [key: string]: any
      }
      const datas = reactive<datasModle>({
        uploadServer: ``,
        fileList: [],
        nextId: '',
        token: '',
        popVisible: false,
        configAttachmentId: '' // 附件模板Id
      })
      watch(
        () => datas.nextId,
        (v) => {
          ctx.emit('update:objectId', v)
        }
      )
      watch(
        () => props.objectId,
        (v) => {
          getFileList()
        }
      )
      const fileAccept = computed(() => {
        if (props.viewType === 'list') {
          return 'image/png,image/gif,image/jpeg,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,aplication/zip,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint'
        }
        return props.accept
      })
      const showUpload = computed(() => {
        return props.limit <= 0 || props.limit > datas.fileList.length
      })
      const imageStyle = computed(() => {
        let height = props.imageHeight
        if (!isNaN(height)) {
          height = `${height}px`
        }
        const width = `${(1 / props.imageColumn) * 100}%`
        return {
          width,
          height
        }
      })

      watch(
        () => datas.fileList,
        () => {
          const { configAttachmentId } = datas
          const { objectId } = props
          const successFile = datas.fileList
            .filter((e: any) => e.uploadStatus === 'success')
            .map((e: any) => {
              const obj = { ...e.response }
              if (configAttachmentId) {
                obj.configAttachmentId = configAttachmentId
              }
              if (objectId) {
                obj.modelId = objectId
              }
              return obj
            })
          nextTick(() => {
            ctx.emit('update:modelValue', successFile)
          })
        },
        { deep: true }
      )
      const getId = () => {
        queryId().then((res: any) => {
          datas.nextId = res.data
        })
      }
      const getConfigId = async () => {
        datas.configAttachmentId = await getAttachmentTemplate(props.typeCode)
      }
      const handlerFileClick = async () => {
        if (props.viewType === 'image') {
          if (process.env.QMP_EVN !== 'development') {
            datas.popVisible = true
            return
          }
        }
        fileEl.value.value = null
        fileEl.value.click()

        /* if (process.env.QMP_EVN === 'development') {
          // 仅PC有效
          fileEl.value.value = null
          fileEl.value.click()
        } else {
          datas.popVisible = true
        } */
      }
      const handlerFileChange = (e: any) => {
        datas.popVisible = false
        if (e.target.files.length === 0) {
          return
        }
        for (const file of e.target.files) {
          uploader(file)
        }
      }
      const getXhr = (method: string, url: string) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url)
        xhr.withCredentials = true
        xhr.setRequestHeader('Authorization', datas.token)
        xhr.responseType = 'json'
        return xhr
      }
      const uploader = (file: any) => {
        const isReuploader = !!file.rawFile
        let i: any
        if (isReuploader) {
          i = datas.fileList.indexOf(file)
          file = file.rawFile
        }
        const _file = {
          rawFile: file,
          response: null,
          uploadStatus: 'uploading',
          progress: 0,
          uid: URL.createObjectURL(new Blob([])).toString().split('/').pop()
        }
        const data = new FormData()
        for (const key in props.otherParams) {
          data.append(key, props.otherParams[key])
        }
        if (props.objectId || datas.nextId) {
          data.append('modelId', props.objectId || datas.nextId)
        }
        if (datas.configAttachmentId) {
          data.append('configAttachmentId', datas.configAttachmentId)
        }
        data.append('file', file)
        isReuploader ? datas.fileList.splice(i, 1, _file) : datas.fileList.push(_file)

        const xhr = getXhr('POST', `${datas.uploadServer}/app/v1/file/uploadFile`)
        xhr.onreadystatechange = (e: any) => {
          if (xhr.readyState === 4) {
            if (xhr.status === 200 && xhr.response.success === 1) {
              _file.response = xhr.response.data
              _file.uploadStatus = 'success'
              updateDatasFile(_file)
            } else {
              proxy.$toast('文件上传失败!')
              _file.uploadStatus = 'failed'
              updateDatasFile(_file)
            }
          }
        }
        xhr.upload.onprogress = (e) => {
          const progress = Number.parseFloat(((e.loaded / e.total) * 100).toFixed(2))
          _file.progress = progress
          updateDatasFile(_file)
        }
        xhr.send(data)
      }
      const updateDatasFile = (f: any) => {
        const list = [...datas.fileList]
        const i = list.findIndex((v) => v.uid === f.uid)
        if (i > -1) {
          list[i] = f
        }
        datas.fileList = list.map((v: any) => v)
      }
      const getKey = (file: any) => {
        if (file.uid) {
          return file.uid
        }
        if (file.response) {
          return file.response.id
        }
        return URL.createObjectURL(new Blob([])).toString().split('/').pop()
      }
      const getFileName = (file: any) => {
        if (file.response) {
          return file.response.attachmentName
        }
        if (file.rawFile) {
          return file.rawFile.name
        }
      }
      const getFileUrl = (file: any) => {
        if (file.response) {
          return formatURL(file.response.attachmentUrl)
        }
        if (file.rawFile) {
          return formatURL(file.rawFile.attachmentUrl)
        }
      }
      const handlerRemove = (file: any) => {
        const i = datas.fileList.indexOf(file)
        if (!props.delSource || file.uploadStatus === 'failed') {
          i > -1 && datas.fileList.splice(i, 1)
          return
        }
        if (!file.response.useId) {
          proxy.$toast('没有useId无法删除!')
          return
        }
        proxy.$dialog
          .confirm({
            title: '提示',
            message: '确认删除?'
          })
          .then(() => {
            // on confirm
            const xhr = getXhr('DELETE', `${datas.uploadServer}/app/v1/attachment/deleteByAttachmentUseIds`)
            xhr.setRequestHeader('Content-Type', 'application/json')
            xhr.onreadystatechange = (e: any) => {
              if (xhr.readyState === 4) {
                const data = xhr.response
                if (xhr.status === 200 && data.success === 1) {
                  i > -1 && datas.fileList.splice(i, 1)
                  proxy.$toast('删除成功!')
                  // this.getFileList()
                } else {
                  proxy.$toast('删除失败!')
                }
              }
            }
            const data = [file.response.useId]
            xhr.send(JSON.stringify(data))
          })
          .catch(() => {
            // on cancel
          })
      }

      const getFileList = async () => {
        if (!props.objectId) return
        let listRecords = await queryAttachmentList(props.objectId)
        if (datas.configAttachmentId) {
          listRecords = listRecords.filter((v: any) => v.configAttachmentId === datas.configAttachmentId)
        }
        if (props.typeCode) {
          listRecords = listRecords.filter((v: any) => v.attachmentTypeCode === props.typeCode)
          // console.log('当前查询附件：', props.typeCode, listRecords.length)
        }
        datas.fileList = listRecords.map((e: any) => {
          return createFileByResponseObj(e)
        })
      }
      const getFileListByXHR = async () => {
        if (!props.objectId) return
        const xhr = getXhr('GET', `${datas.uploadServer}/app/v1/shopAttachment/query/${props.objectId}`)
        xhr.onreadystatechange = (e: any) => {
          if (xhr.readyState === 4) {
            const res = xhr.response
            if (xhr.status === 200 && res.success === 1) {
              let listRecords = res.data || []
              if (datas.configAttachmentId) {
                listRecords = listRecords.filter((v: any) => v.configAttachmentId === datas.configAttachmentId)
              }
              datas.fileList = listRecords.map((e: any) => {
                return createFileByResponseObj(e)
              })
            }
          }
        }
        /*  const filtersRaw = [
          { id: 'objectId', property: 'objectId', value: props.objectId, operator: '=' },
          { id: 'state', property: 'state', value: 1, operator: '=' }
        ]

        for (const key in props.otherParams) {
          filtersRaw.push({ id: `${key}0`, property: key, value: props.otherParams[key], operator: '=' })
        }
        const data: any = new FormData()
        data.append('pageFlag', false)
        data.append('filtersRaw', JSON.stringify(filtersRaw))
        xhr.send(data) */
        // xhr.send()
      }
      const handlerReupload = (file: any) => {
        uploader(file)
      }
      const validate = (required: boolean) => {
        return new Promise((resolve, reject) => {
          const t = datas.fileList.every((e: any) => e.uploadStatus === 'success')
          if (!t) {
            reject(
              datas.fileList
                .filter((e: any) => e.uploadStatus !== 'success')
                .map((e: any) => `文件【${getFileName(e)}】${e.uploadStatus === 'failed' ? '上传失败' : '正在上传'}`)
            )
          } else if (required && datas.fileList.length === 0) {
            reject('')
          } else {
            resolve('')
          }
        })
      }
      const createFileByResponseObj = (response: any) => {
        return {
          rawFile: null,
          response,
          uploadStatus: 'success',
          progress: 100.0,
          uid: URL.createObjectURL(new Blob([])).toString().split('/').pop()
        }
      }
      const loadData = async () => {
        if (props.modelValue.length > 0) {
          datas.fileList = props.modelValue.map((e: any) => {
            return createFileByResponseObj(e)
          })
        }
        if (!props.readonly) {
          props.autoGetId && getId()
          props.typeCode && (await getConfigId())
        }

        getFileList()

        const { token } = await proxy.$nativeHandler()
        datas.token = token
      }
      const handlerDownload = (f: any) => {
        const data = f.response
        // downAttachment(data)
        createDownload(data)
      }
      // 非本地开发时，通过底座方法获取当前对应应用、对应环境的接口基础地址
      const getUploadServer = async () => {
        const res = await proxy.$nativeHandler('getServerUrl', '')
        datas.uploadServer = res && res.data ? `${res.data}${programmeFlag}` : `/${programmeFlag}`
      }

      onMounted(() => {
        // fileEl = document.querySelector('#file-input')
      })
      getUploadServer()
      loadData()
      return {
        ...toRefs(datas),
        fileAccept,
        fileEl,
        handlerFileClick,
        handlerFileChange,
        handlerReupload,
        handlerRemove,
        getFileName,
        getFileUrl,
        getKey,
        showUpload,
        imageStyle,
        validate,
        handlerDownload
      }
    }
  })
</script>
<style lang="less" scoped>
  .attachment-uploader {
    .file-list {
      .file-list-item {
        list-style: none;
        padding: 0;
        margin: 0;
        li {
          padding: 4px 0;
          margin-bottom: 12px;
          font: var(--font-main-body);
          color: var(--texticon-default-normal);
        }
        .cus-progress {
          padding: 8px 0;
        }
        .filename {
          display: flex;
          align-items: center;
          justify-content: space-between;
          .name {
            white-space: nowrap;
            flex: 1;
            max-width: calc(100% - 40px);
            overflow: hidden;
            text-overflow: ellipsis;
          }
          .operator {
            margin-left: 5px;
            .btn {
              width: 20px;
              height: 20px;
              display: flex;
              align-items: center;
              justify-content: center;
            }
            & > * {
              cursor: pointer;
              font-size: 20px;
            }
            & > *:hover {
              color: var(--state-success);
            }
            .remove {
              // color: var(--state-error);
              color: var(--texticon-default-stronger);
            }
          }
        }
      }
    }
    .operator-area {
      .upload-btn {
        width: 48px;
        height: 48px;
        background: var(--bg-global-weak);
        border-radius: 4px;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
      }

      /deep/.qmp-icon {
        font-size: 24px;
        color: var(--texticon-default-weak);
        cursor: pointer;
        &:hover {
          color: var(--texticon-color-brand-norma);
        }
      }
    }

    .file-images-card {
      display: flex;
      flex-wrap: wrap;
      .f-img-item {
        display: inline-block;
        position: relative;
        overflow: hidden;
        min-width: 50px;
        padding: 4px;
        box-sizing: border-box;
      }
      .cus-progress {
        position: absolute;
        left: 0;
        right: 0;
        top: 50%;
        transform: translateY(-50%);
      }
      .img-content {
        position: relative;
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        overflow: hidden;
        img {
          max-width: 100%;
          max-height: 100%;
          object-fit: cover;
        }
        .operator {
          position: absolute;
          left: 0;
          right: 0;
          bottom: 0;
          padding: 4px;
          background: var(--mask-white-normal);
          z-index: 1;
          display: flex;
          justify-content: center;
          align-items: center;
        }
      }
    }
  }
</style>
